﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AMS.Core
{
    public class SimpleIoCC
    {
        // do not make this a Singleton!  Too accessible!

        private Dictionary<Type, object> _registry;

        public SimpleIoCC()
        {
            _registry = new Dictionary<Type, object>();
        }

        public void Register<T>(T concreteInstance)
        {
            if (_registry.ContainsKey(typeof(T)))
            {
                if (concreteInstance == null) // unregister
                    _registry.Remove(typeof(T));
                else // re-map
                    _registry[typeof(T)] = concreteInstance;
            }
            else // map
                _registry.Add(typeof(T), concreteInstance);
        }

        public T GetService<T>()
        {
            if (_registry.ContainsKey(typeof(T)))
                return (T)_registry[typeof(T)];
            else
            {
                object o = FindAlternate(typeof(T));
                if (o != null)
                    return (T)o;
            }

            //return default(T);
            string message = String.Format("no concrete instance of type {0} is registered", typeof(T).ToString());
            throw new ArgumentException(message); // ExceptionHandler?
        }

        private object FindAlternate(Type lookingFor)
        {
            // see if any registered Type is a sub-type of lookingFor
            foreach (Type t in _registry.Keys)
            {
                if (lookingFor.IsAssignableFrom(t))
                {
                    // just return first one found
                    return _registry[t];
                }
            }

            // see if any registered concrete class implements lookingFor
            foreach (object o in _registry.Values)
            {
                if (lookingFor.IsAssignableFrom(o.GetType()))
                {
                    return o;
                }
            }

            return null;
        }

        // more like Inversion of Control, but where do I call this?
        public void InjectDependency<T>(IDependencySetter<T> injectionTarget)
        {
            injectionTarget.SetDependency(GetService<T>());
        }
    }
}
